package org.tru42.signal.lang;

public class NamedBitSet extends Number {

	private static final long serialVersionUID = 4124877238480402395L;

	private long value;
	
	private final String[] names;
	
	private int size;
	
	public NamedBitSet(int length) {
		if(length > Long.SIZE)
			size = Long.SIZE;
		else
			size = length;
		names = new String[size];
		for(int i=0; i<size; i++)
			names[i] = String.valueOf(i);
	}
	
	public NamedBitSet(Number num) {
		this(getSize(num));
		value = num.longValue();
	}
	
	public String getName(int index) {
		if(index >= 0 && index < size)
			return names[index];
		else
			return "";
	}
	
	public void setName(int index, String name) {
		if(index >= 0 && index < size)
			names[index] = name;
	}
	
	public boolean get(int index) {
		return (value & (1<<index)) != 0;
	}
	
	public NamedBit[] getBits() {
		NamedBit[] bits = new NamedBit[size];
		for(int i=0; i<size; i++)
			bits[i] = new NamedBit(names[i], get(i));
		return bits;
	}
	
	/**
	 * Set bit at index to false
	 * @param index index of the bit to set to false
	 */
	public void clear(int index) {
		value &= ~(1<<index);
	}
	
	/**
	 * Set bit at index to true.
	 * @param index	index of the bit to set to true
	 */
	public void set(int index) {
		value |= (1<<index);
	}
	
	/**
	 * Set bit at index to value.
	 * @param index index of the bit to set to value
	 * @param value value to set
	 */
	public void set(int index, boolean value) {
		if(value)
			set(index);
		else
			clear(index);
	}
	
	public void set(Number num) {
		value = num.longValue();			
	}
	
	public int size() {
		return size;
	}
	
	public byte[] toByteArray() {
		byte[] data = new byte[(int) Math.ceil(size/8.0)];
		for(int i=0; i<size; i++)
			data[i/8] |= (1<<(i%8));
		return data;
	}
	
	/**
	 * Get best fitting number.
	 * @return Number that can hold all the bits.
	 */
	public Number numberValue() {
		if(size <= Byte.SIZE)
			return new Byte((byte) value);
		if(size <= Short.SIZE)
			return new Short((short) value);
		if(size <= Integer.SIZE)
			return new Integer((short) value);
		return value;
	}
	
	@Override
	public double doubleValue() {
		return Long.valueOf(value).doubleValue();
	}

	@Override
	public float floatValue() {
		return Long.valueOf(value).floatValue();
	}

	@Override
	public int intValue() {
		return Long.valueOf(value).intValue();
	}

	@Override
	public long longValue() {
		return value;
	}
	
	public static int getSize(Number num) {
		if(num instanceof Byte)
			return Byte.SIZE;
		if(num instanceof Short)
			return Short.SIZE;
		if(num instanceof Integer || num instanceof Float)
			return Integer.SIZE;
		if(num instanceof Long || num instanceof Double)
			return Long.SIZE;
		return 0;
	}

	@Override
	public String toString() {
		return Long.toBinaryString(value);
	}
	
}
